package org.jetbrains.plugins.scala.lang.resolveSemanticDb

import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.ThrowableRunnable
import junit.framework.TestCase
import org.jetbrains.plugins.scala.extensions.{PathExt, StringExt}
import org.jetbrains.plugins.scala.lang.resolveSemanticDb.ReferenceComparisonTestBase.Result
import org.jetbrains.plugins.scala.lang.resolveSemanticDb.configurations._

import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Path}
import scala.annotation.tailrec
import scala.collection.mutable

object ReferenceComparisonTestsGenerator_Scala3  {
  /**
   * Generates [[org.jetbrains.plugins.scala.lang.resolveSemanticDb.generated.ReferenceComparisonTest_Scala3_LTS]]
   */
  final class TestCase_Scala3_LTS extends ScriptTestCaseBase(ReferenceComparisonTestConfig_Scala3_LTS)

  /**
   * Generates [[org.jetbrains.plugins.scala.lang.resolveSemanticDb.generated.ReferenceComparisonTest_Scala3_Newest]]
   */
  final class TestCase_Scala3_Newest extends ScriptTestCaseBase(ReferenceComparisonTestConfig_Scala3_Newest)

  abstract class ScriptTestCaseBase(config: ReferenceComparisonTestConfig) extends TestCase {
    val excluded: Set[String] = Set(
      "large", "large2" // they're just very large with ~10k references/definitions
    )

//    val testOutputPath: Path =
//      Paths.get(TestUtils.findCommunityRoot)
//        .resolve("scala/scala-impl/test/org/jetbrains/plugins/scala/lang/resolveSemanticDb/generated/ReferenceComparisonTest_Scala3.scala")

    def test_run_generator(): Unit = {

      val builder = new mutable.StringBuilder

      builder ++=
        s"""
           |package org.jetbrains.plugins.scala.lang.resolveSemanticDb
           |package generated
           |
           |import org.jetbrains.plugins.scala.ScalacTests
           |import org.junit.experimental.categories.Category
           |
           |/**
           | * Tests reference resolving for test cases imported from dotty repository
           | *
           | * Important: This file is generated by [[org.jetbrains.plugins.scala.lang.resolveSemanticDb.ReferenceComparisonTestsGenerator_Scala3]]<br>
           | * Modify [[ReferenceComparisonTestBase.collectFeaturesIn]] to add more tags to tests
           | */
           |//noinspection NameBooleanParameters
           |@Category(Array(classOf[ScalacTests]))
           |class ${config.testClassName} extends ReferenceComparisonTestBase(${config.selfQualifiedName}) {
           |""".stripMargin

      var testedCases = 0
      var successes = 0
      var result = Result.empty

      def testNameFromFilePath(path: Path): String =
        path.getFileName.toString.replaceAll("(\\.[0-9a-f]{6})?\\.semdb$", "")

      val testOutPaths = config.outPath.children()
        .sortBy(testNameFromFilePath)((x, y) => StringUtil.naturalCompare(x, y))

      val originalTestNames = testOutPaths.map(testNameFromFilePath).toSet
      val usedTestNames = mutable.Set.empty[String]

      for {
        testOutPath <- testOutPaths
        testName = testNameFromFilePath(testOutPath)
        if !excluded(testName)
      } {

        val test: ReferenceComparisonTestBase = new ReferenceComparisonTestBase(config) {
          override def runTestRunnable(testRunnable: ThrowableRunnable[Throwable]): Unit = {
            val res = runTestToResult(testName)
            testedCases += 1
            val success = res.problems.isEmpty
            if (success)
              successes += 1
            result += res

            val normalizedTestName = testName.replace('-', '_').replace('.', '_')
            if(!normalizedTestName.forall(Character.isJavaIdentifierPart)) {
              throw new IllegalArgumentException(s"Invalid test name: $normalizedTestName")
            }

            @tailrec
            def createUniqueNameId(name: String, n: Int = 1): String = {
              if (usedTestNames.contains(name)) {
                createUniqueNameId(s"${name}_$n", n)
              } else name
            }

            val finalTestName = createUniqueNameId(normalizedTestName)
            usedTestNames += finalTestName

            val testId = s"test_$finalTestName"
            val tags =
              if (res.tags.isEmpty) ""
              else res.tags.mkString(" // #", ", #", "")
            builder ++= raw"""  def $testId(): Unit = doTest("$testName", $success)$tags"""
            builder += '\n'

            val progress = testedCases.toDouble / testOutPaths.size.toDouble * 100
            val successRate = (successes.toDouble / testedCases.toDouble) * 100
            println(
              s"(${progress.toInt}%) " +
                s"$testName: $success (${successRate.toInt}% $successes/$testedCases) " +
                s"| problems: ${result.problems.size} " +
                s"| refs: ${result.refCount} " +
                s"| failed to resolve: ${result.failedToResolve} (${(result.failedToResolve.toDouble / result.refCount * 100.0).toInt}%) " +
                s"| not tested: ${result.refCount - result.failedToResolve - result.testedRefs}) " +
                s"| complete correct: ${result.completeCorrect} " +
                s"| partial correct: ${result.partialCorrect} " +
                s"| incorrect resolve: ${result.incorrectResolves}"
            )
          }
        }

        test.run()
      }

      if (testedCases < 100) {
        System.err.println(
          """###
            |### Did not execute any generator tests!
            |### Try setting module to scalaCommunity/scalaUltimate and copy vm options from one of the run configurations
            |###""".stripMargin)
        System.exit(1)
      }

      builder ++= "}\n"

      println(s"Print to ${config.testClassPath}...")
      Files.writeString(config.testClassPath, builder, StandardCharsets.UTF_8)
      println("Done.")
    }
  }
}
